<?php
/**
 * Created by PhpStorm.
 * User: syzhou
 * Date: 2019-08-24
 * Time: 12:17
 */

namespace App\Services;


use App\Project;
use App\ReviewerGroup;
use App\ReviewerGroupProject;
use App\ReviewerGroupReviewer;
use App\ReviewerScoreProject;
use App\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class ReviewerGroupService
{
    //const REVIEWER_GROUP_REVIEWER_COUNT = 3;

    /**
     * @edit 2020-06-03 专家组人员调整至 5 人
     */
    const REVIEWER_GROUP_REVIEWER_COUNT = 5;

    /**
     * @var WorkflowService
     */
    private $workflowSvc;

    /**
     * ReviewerGroupService constructor.
     * @throws \Exception
     */
    public function __construct()
    {
        $this->workflowSvc = new WorkflowService();
    }

    /**
     * @param $largeGroupId
     * @return array
     *
     * - code: int
     *     0: 成功
     *     2: 数据库操作失败
     *     3: 现在不是分配评审阶段，不能创建专家组
     *     4: 挑选专家时出错
     * - msg: string
     * - reviewer_group: stdClass
     *   - id
     *   - large_group_id
     *   - name
     *   - reviewer_group_order
     *   - keywords
     *   - reviewers: array[stdClass]
     *   - project_count: 0
     *   - projects: []
     */
    public function createReviewerGroup($largeGroupId)
    {
        if ($this->workflowSvc->notAtPhases([WorkflowService::PHASE_ASSIGN_REVIEW], $largeGroupId)) {
            return ['code' => 3, 'msg' => '现在不是分配评审阶段，不能创建专家组'];
        }

        try {
            $largeGroup = DB::table('large_groups')->find($largeGroupId);;
        } catch (\Exception $e) {
            return ['code' => 2, 'msg' => '数据库错误'];
        }
        $selectReviewersResult = $this->selectReviewersToJoinReviewerGroup($largeGroup, self::REVIEWER_GROUP_REVIEWER_COUNT);
        if ($selectReviewersResult['code'] != 0) {
            return [
                'code' => 4,
                'msg' => "挑选专家时出错(code={$selectReviewersResult['code']}, msg={$selectReviewersResult['msg']})",
            ];
        }
        $reviewers = $selectReviewersResult['reviewers'];
        try {
            DB::beginTransaction();
            $max_reviewer_group_order = DB::table('reviewer_groups')
                ->where('large_group_id', $largeGroupId)
                ->max('reviewer_group_order');
            $max_reviewer_group_order = $max_reviewer_group_order !== null ? $max_reviewer_group_order : 0;
            $reviewer_group_order = $max_reviewer_group_order + 1;
            $name = $this->genReviewerGroupName($largeGroup->name, $reviewer_group_order);
            $reviewerGroup = ReviewerGroup::create([
                'large_group_id' => $largeGroupId, 'name' => $name, 'reviewer_group_order' => $reviewer_group_order, 'keywords' => null
            ]);
            $reviewerGroup = json_decode(json_encode($reviewerGroup));  // Model => stdClass
            $reviewerGroup->project_count = 0;
            $reviewerGroup->projects = [];
            $reviewer_group_reviewers = array_map(function ($key, $reviewer) use ($reviewerGroup, $largeGroupId) {
                return ['reviewer_id' => $reviewer->id, 'reviewer_group_id' => $reviewerGroup->id,
                    'large_group_id' => $largeGroupId, 'can_attend_review' => 0, 'reviewer_order' => $key + 1];
            }, array_keys($reviewers), $reviewers);
            DB::table('reviewer_group_reviewers')->insert($reviewer_group_reviewers);
            $reviewerIds = array_map(function ($reviewer) {
                return $reviewer->id;
            }, $reviewers);
            $reviewerGroupReviewers = DB::table('reviewer_group_reviewers')
                ->whereIn('reviewer_id', $reviewerIds)
                ->orderBy('reviewer_order')
                ->get()->toArray();
            foreach ($reviewerGroupReviewers as $reviewerGroupReviewer) {
                $reviewerId = $reviewerGroupReviewer->reviewer_id;
                foreach ($reviewers as $reviewer) {
                    if ($reviewerId == $reviewer->id) {
                        $reviewerGroupReviewer->name = $reviewer->name;
                        $reviewerGroupReviewer->prof_title = $reviewer->prof_title;
                        $reviewerGroupReviewer->tel = $reviewer->tel;
                        $reviewerGroupReviewer->work_org_name = $reviewer->work_org_name;
                        break;
                    }
                }
            }
            $reviewerGroup->reviewers = $reviewerGroupReviewers;
            DB::commit();
            return [
                'code' => 0,
                'msg' => 'ok',
                'reviewer_group' => $reviewerGroup,
            ];
        } catch (\Exception $e) {
            Log::error('[ReviewerGroupService::createReviewerGroup]', ['exception' => $e]);
            try {
                DB::rollBack();
            } catch (\Exception $e) {

            }
            return [
                'code' => 2,
                'msg' => '数据库错误',
            ];
        }
    }

    /**
     * @param $reviewerGroupId
     * @return array
     * 模式:
     * - code: int 错误码
     *     0: 成功
     *     2: 数据库错误
     *     3: 不存在指定的专家组
     *     4: 现在不是分配评审阶段，不能删除专家组
     * - msg: string
     * - updated_reviewer_groups: array[stdClass] 受删除影响，内容发生变化的专家组
     */
    public function deleteReviewerGroup($reviewerGroupId)
    {

        $inTransaction = false;
        try {
            if (($deletedReviewerGroup = ReviewerGroup::find($reviewerGroupId)) === null) {
                return [
                    'code' => 3,
                    'msg' => "不存在 id=$reviewerGroupId 的专家组"
                ];
            }
            $largeGroupId = $deletedReviewerGroup->large_group_id;
            if ($this->workflowSvc->notAtPhases([WorkflowService::PHASE_ASSIGN_REVIEW], $largeGroupId)) {
                return ['code' => 4, 'msg' => '现在不是分配评审阶段，不能删除专家组'];
            }

            DB::beginTransaction();
            $inTransaction = true;
            $deletedReviewerGroupOrder = $deletedReviewerGroup->reviewer_group_order;
            $assignedProjectIds = DB::table('reviewer_group_projects')
                ->where('reviewer_group_id', $reviewerGroupId)
                ->pluck('project_id')->toArray();
            if (!empty($assignedProjectIds)) {
                ReviewerScoreProject::where('reviewer_group_id', $reviewerGroupId)->delete();
                ReviewerGroupProject::where('reviewer_group_id', $reviewerGroupId)->delete();
                Project::whereIn('id', $assignedProjectIds)->update(['status_id' => Project::STATUS_ASSIGN_REVIEW_TO_ASSIGN]);
            }
            ReviewerGroupReviewer::where('reviewer_group_id', $reviewerGroupId)->delete();
            $deletedReviewerGroup->delete();

            // 修改被删除专家组后面专家组的顺序和组名
            $updatedReviewerGroups = [];  // 受删除影响，内容发生变化的专家组
            $nextReviewerGroups = ReviewerGroup::where('large_group_id', $largeGroupId)
                ->where('reviewer_group_order', '>', $deletedReviewerGroupOrder)
                ->orderBy('reviewer_group_order')
                ->get();
            if (!empty($nextReviewerGroups)) {
                $largeGroupName = (new LargeGroupService())->getLargeGroupById($largeGroupId)->name;
                foreach ($nextReviewerGroups as $group) {
                    $group->reviewer_group_order--;
                    $group->name = $this->genReviewerGroupName($largeGroupName, $group->reviewer_group_order);
                    $group->save();
                    $updatedReviewerGroups[] = (object)[
                        'id' => $group->id,
                        'reviewer_group_order' => $group->reviewer_group_order,
                        'name' => $group->name
                    ];
                }
            }

            DB::commit();
            return [
                'code' => 0,
                'msg' => 'ok',
                'updated_reviewer_groups' => $updatedReviewerGroups,
            ];
        } catch (\Exception $e) {
            Log::error('', ['exception' => $e]);
            if ($inTransaction) {
                try {
                    DB::rollBack();
                } catch (\Exception $e) {
                };
            }
            return [
                'code' => 2,
                'msg' => '数据库错误',
            ];
        }
    }

    /**
     * @param $reviewerGroupId
     * @param $keywords
     * @return array 模式:
     * - code: int 错误码
     *     0: 成功
     *     2: 数据库错误
     * - msg: string
     */
    public function updateReviewerGroupKeywords($reviewerGroupId, $keywords)
    {
        $keywords = trim($keywords);
        $keywords = mb_ereg_replace('[,:;、，：；\s]+', ',', $keywords);
        try {
            ReviewerGroup::where('id', $reviewerGroupId)
                ->update(['keywords' => $keywords]);
            return [
                'code' => 0,
                'msg' => 'ok',
            ];
        } catch (\Exception $e) {
            return [
                'code' => 2,
                'msg' => '数据库错误',
            ];
        }
    }

    /**
     * @param $reviewerId
     * @param $canAttendReview
     * @return array 模式:
     * - code: int 错误码
     *     0: 成功
     *     2: 数据库错误
     *     3: 现在不是分配评审阶段，不能修改专家的能否参加评审状态
     * - msg: string
     */
    public function updateReviewerCanAttendReview($reviewerId, $canAttendReview)
    {
        $canAttendReview = (int)$canAttendReview;
        $inTransaction = false;
        try {
            $largeGroupId = DB::table('reviewer_group_reviewers')
                ->where('reviewer_id', $reviewerId)
                ->value('large_group_id');
            if ($this->workflowSvc->notAtPhases([WorkflowService::PHASE_ASSIGN_REVIEW], $largeGroupId)) {
                return ['code' => 3, 'msg' => '现在不是分配评审阶段，不能修改专家的能否参加评审状态'];
            }
            DB::beginTransaction();
            $inTransaction = true;
            ReviewerGroupReviewer::where('reviewer_id', $reviewerId)->update(['can_attend_review' => $canAttendReview]);
            if ($canAttendReview) {
                DB::table('excluded_reviewers')->where('reviewer_id', $reviewerId)->delete();
            } else {
                DB::insert("insert ignore into excluded_reviewers (reviewer_id, excluded_reason) values ($reviewerId, '不能参加评审')");
            }
            DB::commit();
            return [
                'code' => 0,
                'msg' => 'ok',
            ];
        } catch (\Exception $e) {
            Log::error('[ReviewerGroupService::updateReviewerCanAttendReview]', ['exception' => $e]);
            if ($inTransaction) {
                try {
                    DB::rollBack();
                } catch (\Exception $e) {

                }
            }
            return [
                'code' => 2,
                'msg' => '数据库错误',
            ];
        }
    }

    /**
     * @param $reviewerId int|string reviewer_group_reviewers.reviewer_id 列
     * @return array 模式:
     * - code: int 错误码
     *     0: 成功
     *     2: 数据库错误
     *     3: 现在不是分配评审阶段，不能替换专家
     *     4: 挑选新专家失败
     * - msg: string
     * - old_reviewer: stdClass
     * - new_reviewer: stdClass
     */
    public function autoReplaceReviewerGroupReviewer($reviewerId)
    {
        try {
            $oldReviewerGroupReviewer = DB::table('reviewer_group_reviewers')->where('reviewer_id', $reviewerId)->first();
            $largeGroupId = $oldReviewerGroupReviewer->large_group_id;
            if ($this->workflowSvc->notAtPhases([WorkflowService::PHASE_ASSIGN_REVIEW], $largeGroupId)) {
                return ['ok' => false, 'code' => 3, 'msg' => '现在不是分配评审阶段，不能替换专家'];
            }

            $selectReviewersResult = $this->selectReviewersToJoinReviewerGroup($largeGroupId, 1);
            if ($selectReviewersResult['code'] != 0) {
                return ['ok' => false, 'code' => 4, 'msg' => "挑选新专家失败(code={$selectReviewersResult['code']}, msg={$selectReviewersResult['msg']})"];
            }
            $newReviewer = $selectReviewersResult['reviewers'][0];
            $newValues = ['reviewer_id' => $newReviewer->id, 'can_attend_review' => 0];
            if (!$this->doReplaceReviewer($reviewerId, $newReviewer->id)) {
                return [
                    'ok' => false,
                    'code' => 5,
                    'msg' => '执行替换时出错',
                ];
            }
            $oldReviewerGroupReviewer->reviewer_id = $newReviewer->id;

            $newReviewerGroupReviewer = (object)array_replace((array)$oldReviewerGroupReviewer, $newValues);
            $newReviewerGroupReviewer->name = $newReviewer->name;
            $newReviewerGroupReviewer->prof_title = $newReviewer->prof_title;
            $newReviewerGroupReviewer->tel = $newReviewer->tel;
            $newReviewerGroupReviewer->work_org_name = $newReviewer->work_org_name;
            return [
                'ok' => true,
                'code' => 0,
                'msg' => 'ok',
                'old_reviewer' => $oldReviewerGroupReviewer,
                'new_reviewer' => $newReviewerGroupReviewer,
            ];
        } catch (\Exception $e) {
            Log::error('', ['exception' => $e]);
            return [
                'ok' => false,
                'code' => 2,
                'msg' => '数据库错误',
            ];
        }
    }

    public function delMeetingReviewer($ReviewerId)
    {
        $DbOperatorFlag = false;
        try {
            $reviewer = User::find($ReviewerId);
            if (is_null($reviewer)) {
                throw new \Exception('专家信息不存在或状态异常');
            }

            $DbOperatorFlag = true;
            DB::beginTransaction();
            $reviewer->is_meeting_reviewer = 0;
            $res = $reviewer->save();
            if (!$res) {
                throw new \Exception('数据库错误【reviewer】');
            }

            $res = DB::table('meeting_review_reviewers')
                ->where('reviewer_id', $ReviewerId)
                ->delete();
            if (!$res) {
                throw new \Exception('数据库错误【meeting_review_reviewers】');
            }

            DB::commit();

            $result = [
                'ok' => true,
                'msg' => '已删除'
            ];

        } catch (\Exception $e) {
            if ($DbOperatorFlag) {
                DB::rollBack();
            }
            $result = [
                'ok' => false,
                'msg' => $e->getMessage()
            ];
        }
        return $result;
    }

    public function selectMeetingReviewer($ReviewerId, $LargeGroupId)
    {
        $DbOperatorFlag = false;
        try {
            $LargeGroupInfo = DB::table('large_groups')
                ->where('id', $LargeGroupId)
                ->first();
            if (is_null($LargeGroupInfo)) {
                throw new \Exception('大组信息不存在或状态异常');
            }

            $reviewer = User::find($ReviewerId);
            if (is_null($reviewer)) {
                throw new \Exception('专家信息不存在或状态异常');
            }

            $insert = [
                'large_group_id' => $LargeGroupId,
                'reviewer_id' => $ReviewerId
            ];

            $DbOperatorFlag = true;

            DB::beginTransaction();
            $reviewer->is_meeting_reviewer = 1;
            $res = $reviewer->save();
            if (!$res) {
                throw new \Exception('数据库错误【reviewer】');
            }

            $res = DB::table('meeting_review_reviewers')
                ->insert($insert);
            if (!$res) {
                throw new \Exception('数据库错误【meeting_review_reviewers】');
            }

            DB::commit();

            $result = [
                'ok' => true,
                'msg' => '已选择'
            ];

        } catch (\Exception $e) {

            if ($DbOperatorFlag) {
                DB::rollBack();
            }
            $result = [
                'ok' => false,
                'msg' => $e->getMessage()
            ];
        }

        return $result;
    }

    public function replaceReviewer($oldReviewerId, $newReviewerId)
    {
        try {
            $oldReviewerGroupReviewer = DB::table('reviewer_group_reviewers')->where('reviewer_id', $oldReviewerId)->first();
            $largeGroupId = $oldReviewerGroupReviewer->large_group_id;
            if ($this->workflowSvc->notAtPhases([WorkflowService::PHASE_ASSIGN_REVIEW], $largeGroupId)) {
                return ['ok' => false, 'code' => 3, 'msg' => '现在不是分配评审阶段，不能替换专家'];
            }
            if ($this->doReplaceReviewer($oldReviewerId, $newReviewerId)) {
                return [
                    'ok' => true,
                    'code' => 0,
                    'msg' => 'ok',
                ];
            } else {
                return [
                    'ok' => false,
                    'code' => 4,
                    'msg' => '执行替换时出错',
                ];
            }
        } catch (\Exception $e) {
            Log::error('', ['exception' => $e]);
            return [
                'ok' => false,
                'code' => 2,
                'msg' => '数据库错误',
            ];
        }
    }

    private function doReplaceReviewer($oldReviewerId, $newReviewerId)
    {
        $inTransaction = false;
        try {
            DB::beginTransaction();
            $inTransaction = true;
            ReviewerGroupReviewer::where('reviewer_id', $oldReviewerId)
                ->update(['reviewer_id' => $newReviewerId, 'can_attend_review' => 0]);
            ReviewerScoreProject::where('reviewer_id', $oldReviewerId)->update(['reviewer_id' => $newReviewerId]);
            DB::commit();
            return true;
        } catch (\Exception $e) {
            Log::error('', ['exception' => $e]);
            if ($inTransaction) {
                try {
                    DB::rollBack();
                } catch (\Exception $e) {
                }
            }
            return false;
        }
    }

    public function getReviewersForReplacement($reviewerId)
    {
        $largeGroupId = ReviewerGroupReviewer::where('reviewer_id', $reviewerId)->value('large_group_id');
        return $this->getCandidateReviewers($largeGroupId);
    }

    /**
     * @param $largeGroupId
     * @return array 模式：
     * - code: int 错误码
     *     0: 成功
     *     2: 数据库错误
     * - msg: string
     * - reviewer_groups: array
     *   - id:
     *   - name
     *   - ...
     *   - reviewers: array[stdClass]
     *   - projects: array[stdClass]
     */
    public function getReviewerGroups($largeGroupId)
    {
        try {
            $reviewerGroups = DB::table('reviewer_groups')
                ->where('large_group_id', $largeGroupId)
                ->orderBy('reviewer_group_order')
                ->get()->toArray();
            if (empty($reviewerGroups)) {
                return [
                    'code' => 0,
                    'msg' => 'ok',
                    'reviewer_groups' => []
                ];
            }
            $reviewGroupId2Key = [];
            foreach ($reviewerGroups as $key => $group) {
                $reviewGroupId2Key[$group->id] = $key;
                $group->project_count = 0;
                $group->reviewers = [];
                $group->projects = [];
            }
            $reviewerGroupIds = array_map(function ($reviewGroup) {
                return $reviewGroup->id;
            }, $reviewerGroups);
            $reviewGroupReviewers = DB::table('reviewer_group_reviewers as a')
                ->whereIn('a.reviewer_group_id', $reviewerGroupIds)
                ->leftJoin('users as b', 'a.reviewer_id', '=', 'b.id')
                ->select('a.*', 'b.username', 'b.password', 'b.name', 'b.prof_title', 'b.tel', 'b.work_org_name')
                ->get();
            $reviewGroupProjects = DB::table('reviewer_group_projects as a')
                ->whereIn('a.reviewer_group_id', $reviewerGroupIds)
                ->leftJoin('applications as b', 'a.project_id', '=', 'b.id')
                ->leftJoin('users as c', 'b.user_id', '=', 'c.id')
                ->select('a.*', 'b.award_id', 'b.project_name', 'c.name as applicant_name')
                ->get();
        } catch (\Exception $e) {
            Log::error('[ReviewerGroupService::getReviewerGroups]', ['exception' => $e]);
            return [
                'code' => 2,
                'msg' => '数据库错误',
            ];
        }
        foreach ($reviewGroupReviewers as $reviewer) {
            $reviewGroupId = $reviewer->reviewer_group_id;
            $group = $reviewerGroups[$reviewGroupId2Key[$reviewGroupId]];
            $group->reviewers[] = $reviewer;
        }
        $awards = config('vars.awards');
        foreach ($reviewGroupProjects as $project) {
            $project->award_name = $awards[$project->award_id]['name'];
            $reviewGroupId = $project->reviewer_group_id;
            $group = $reviewerGroups[$reviewGroupId2Key[$reviewGroupId]];
            $group->project_count++;
            $group->projects[] = $project;
        }

        foreach ($reviewerGroups as $group) {
            usort($group->reviewers, function ($reviewer1, $reviewer2) {
                return $reviewer1->reviewer_order - $reviewer2->reviewer_order;
            });
        }
        foreach ($reviewerGroups as $group) {
            usort($group->projects, function ($project1, $project2) {
                return $project1->id - $project2->id;
            });
        }

        return [
            'code' => 0,
            'msg' => 'ok',
            'reviewer_groups' => $reviewerGroups,
        ];
    }


    public function getMeetingReviewers($large_group_id)
    {
        $reviewerIds = DB::table('meeting_review_reviewers')
            ->where('large_group_id', $large_group_id)
            ->pluck('reviewer_id')
            ->toArray();

        $allReviewers = DB::table('users')
            ->select('id', 'name', 'prof_title', 'tel', 'work_org_name', 'prof_fields', 'username', 'password')
            ->whereIn('id', $reviewerIds)
            ->get()
            ->toArray();

        if (empty($allReviewers)) {
            $allReviewers = [];
        }

        return [
            'ok' => true,
            'code' => 0,
            'msg' => 'ok',
            'list' => $allReviewers,
        ];
    }


    /***
     * 会评专家候选
     * @return array
     * @author: lxy
     * @time: 2020/7/14 2:40 下午
     * @流程:
     * 1.已排除专家
     * 2.查询可选专家
     */
    public function getMeetingCandidateReviewers()
    {
        $excludedReviewerIds = DB::table('excluded_reviewers')->pluck('reviewer_id')->toArray();
        // is_reviewer = 1为专家
        // is_meeting_reviewer = 0 非会评专家（避免一个专家进入多个大组）
        // id not in $excludedReviewerIds 过滤排除专家
        // tel｜work_org_name｜prof_title 为空的信息过滤
        $allReviewers = DB::table('users')
            ->select('id', 'name', 'prof_title', 'tel', 'work_org_name', 'prof_fields','username','password')
            ->where('is_reviewer', 1)
            ->where('is_meeting_reviewer', 0)
            ->whereNotIn('id', $excludedReviewerIds)
            ->where('tel', '<>', '')
            ->where('work_org_name', '<>', '')
            ->where('prof_title', '<>', '')
            ->get()
            ->toArray();

        if (empty($allReviewers)) {
            $allReviewers = [];
        }

        return [
            'ok' => true,
            'code' => 0,
            'msg' => 'ok',
            'candidateReviewers' => $allReviewers,
        ];

    }


    /**
     * @param $largeGroup \stdClass|int|string  大组对象或 大组id
     * @return array
     */
    public function getCandidateReviewers($largeGroup)
    {
        try {
            $allReviewers = DB::table('users')
                ->select('id', 'name', 'prof_title', 'tel', 'work_org_name', 'prof_fields')
                ->where('is_reviewer', 1)
                ->get()->toArray();
            foreach ($allReviewers as $index => $reviewer) {
                $keys = ['tel', 'prof_title', 'work_org_name'];
                foreach ($keys as $key) {
                    if (strlen(trim($reviewer->{$key})) == 0) {
                        unset($allReviewers[$index]);
                        break;
                    }
                }
            }
            $assignedReviewerIds = DB::table('reviewer_group_reviewers')->pluck('reviewer_id')->toArray();
            $excludedReviewerIds = DB::table('excluded_reviewers')->pluck('reviewer_id')->toArray();
        } catch (\Exception $e) {
            Log::error('[ReviewerGroupService::selectReviewersForCreateReviewerGroup] 查询数据库异常', ['exception' => $e]);
            return ['ok' => false, 'code' => 2, 'msg' => '数据库错误'];
        }
        $excludedReviewerIds = array_merge($assignedReviewerIds, $excludedReviewerIds);
        $excludedReviewerIdSet = [];
        foreach ($excludedReviewerIds as $excludedReviewerId) {
            $excludedReviewerIdSet[$excludedReviewerId] = null;
        }
        $candidateReviewers = array_filter($allReviewers, function ($reviewer) use ($excludedReviewerIdSet) {
            return !array_key_exists($reviewer->id, $excludedReviewerIdSet);
        });
        if (!is_object($largeGroup)) {
            $largeGroupId = $largeGroup;
            try {
                $largeGroup = (new LargeGroupService())->getLargeGroupById($largeGroupId);
                if ($largeGroup === null) {
                    return ['ok' => false, 'code' => 3, 'msg' => "找不到 id=$largeGroupId 的大组"];
                }
            } catch (\Exception $e) {
                return ['ok' => false, 'code' => 2, 'msg' => '数据库错误'];
            }
        } else {
            $largeGroupId = $largeGroup->id;
        }

        // 根据大组对专家的专业领域要求筛选专家
        Log::debug("[ReviewerGroupService::selectReviewersForCreateReviewerGroup] " . count($candidateReviewers) . " candidate reviewers for large group $largeGroupId before check professional fields");
        $largeGroupAttrs = json_decode($largeGroup->attrs, true);
        if (!empty($largeGroupAttrs['reviewer']['prof_fields'])) {
            $requiredProfFields = $largeGroupAttrs['reviewer']['prof_fields'];
            Log::debug("[ReviewerGroupService::selectReviewersForCreateReviewerGroup] select reviewer for large group #$largeGroupId", ['requiredProfFields' => $requiredProfFields]);
            $candidateReviewers = array_filter($candidateReviewers, function ($reviewer) use ($requiredProfFields) {
                $reviewerProfFields = array_filter(explode(',', $reviewer->prof_fields));
                foreach ($reviewerProfFields as $reviewerProfField) {
                    if (in_array($reviewerProfField, $requiredProfFields)) {
                        return true;
                    }
                }
                Log::debug("[ReviewerGroupService::selectReviewersForCreateReviewerGroup] reviewer #{$reviewer->id} not match on prof_fields($reviewer->prof_fields)", []);
                return false;
            });
        }
        $candidateReviewers = array_values($candidateReviewers);
        Log::debug("[ReviewerGroupService::selectReviewersForCreateReviewerGroup] " . count($candidateReviewers) . " candidate reviewers for large group $largeGroupId");
        return [
            'ok' => true,
            'code' => 0,
            'msg' => 'ok',
            'candidateReviewers' => $candidateReviewers,
        ];
    }

    /**
     * @param $largeGroup \stdClass|int|string  大组对象或 大组id
     * @param $count int
     * @return array 模式:
     * - code: int 错误码
     *     0: 成功
     *     2: 数据库错误
     *     3: 找不到大组
     *     4: 没有足够的专家
     * - msg: string
     * - reviewers: array[stdClass]
     */
    public function selectReviewersToJoinReviewerGroup($largeGroup, $count)
    {
        $ret = $this->getCandidateReviewers($largeGroup);
        if ($ret['code'] != 0) {
            return ['code' => 3, 'msg' => '获取候选专家失败'];
        }
        $candidateReviewers = $ret['candidateReviewers'];
        if (count($candidateReviewers) < self::REVIEWER_GROUP_REVIEWER_COUNT) {
            return ['code' => 4, 'msg' => '没有足够的专家'];
        }

        $reviewerKeys = array_rand($candidateReviewers, $count);
        if (!is_array($reviewerKeys)) {
            $reviewerKeys = [$reviewerKeys];
        }
        $reviewers = array_map(function ($key) use ($candidateReviewers) {
            return $candidateReviewers[$key];
        }, $reviewerKeys);
        return ['code' => 0, 'msg' => 'ok', 'reviewers' => $reviewers];
    }

    private function genReviewerGroupName($largeGroupName, $reviewerGroupOrder)
    {
        $n = mb_strlen($largeGroupName);
        if (mb_substr($largeGroupName, $n - 1, 1) === '组') {
            $prefix = mb_substr($largeGroupName, 0, $n - 1);
        } else {
            $prefix = $largeGroupName;
        }
        return "$prefix{$reviewerGroupOrder}组";
    }
}